home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Linux / Kubuntu 8.10 / kubuntu-8.10-desktop-i386.iso / casper / filesystem.squashfs / usr / bin / jockey-kde < prev    next >
Text File  |  2008-10-24  |  19KB  |  494 lines

  1. #!/usr/bin/python
  2.  
  3. # (c) 2007 Canonical Ltd.
  4. #
  5. # This program is free software; you can redistribute it and/or modify
  6. # it under the terms of the GNU General Public License as published by
  7. # the Free Software Foundation; either version 2 of the License, or
  8. # (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. # GNU General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU General Public License along
  16. # with this program; if not, write to the Free Software Foundation, Inc.,
  17. # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  18.  
  19. '''KDE user interface implementation.'''
  20.  
  21. import sys
  22. import os.path
  23. import re
  24.  
  25. from PyQt4.QtCore import SIGNAL
  26. from PyQt4.QtCore import QTimer
  27. import PyQt4.QtGui
  28. from PyKDE4.kdecore import *
  29. from PyKDE4.kdeui import *
  30.  
  31. import jockey.ui
  32. from jockey.oslib import OSLib
  33. import jockey.kdeui.ui_ManagerWindowKDE4
  34. import jockey.kdeui.ui_ProgressDialog
  35. import jockey.kdeui.ui_LicenseDialog
  36.  
  37. class JockeyTreeWidgetItem(PyQt4.QtGui.QTreeWidgetItem):
  38.     '''A subclassed QTreeWidgetItem that can store its handler.
  39.  
  40.     A workaround for the Model/View framework which will be implemented later
  41.     on.
  42.     '''
  43.     def __init__(self, handler_id, parent=None):
  44.         super(JockeyTreeWidgetItem, self).__init__(parent)
  45.         self.handler_id = handler_id
  46.  
  47. class ManagerWindowKDE4(PyQt4.QtGui.QDialog, jockey.kdeui.ui_ManagerWindowKDE4.Ui_manager_window):
  48.     def __init__(self, parent=None):
  49.         super(ManagerWindowKDE4, self).__init__(parent)
  50.         self.setupUi(self)
  51.  
  52. class ProgressDialog(PyQt4.QtGui.QDialog, jockey.kdeui.ui_ProgressDialog.Ui_progress_dialog):
  53.     def __init__(self, parent=None):
  54.         super(ProgressDialog, self).__init__(parent)
  55.         self.setupUi(self)
  56.  
  57. class LicenseDialog(PyQt4.QtGui.QDialog, jockey.kdeui.ui_LicenseDialog.Ui_dialog_licensetext):
  58.     def __init__(self, parent=None):
  59.         super(LicenseDialog, self).__init__(parent)
  60.         self.setupUi(self)
  61.  
  62. class KDEUI(jockey.ui.AbstractUI):
  63.     '''KDE user interface implementation.'''
  64.     
  65.     keyb_trans_re = re.compile('(?<! _) _ (?! _)', re.X)
  66.     
  67.     def convert_keybindings(self, str):
  68.         return self.keyb_trans_re.sub('&', str)
  69.         
  70.     def ui_init(self):
  71.         '''Initialize UI.'''
  72.         
  73.         self.mw = ManagerWindowKDE4()     
  74.  
  75.         # set icon 
  76.         if os.path.exists('/usr/share/icons/hicolor/22x22/apps/jockey-kde.png') and os.path.exists('/usr/share/icons/hicolor/48x48/apps/jockey-kde.png'):
  77.             icon = KIcon('jockey-kde.png')
  78.             largeIcon = KIcon('jockey-kde.png')
  79.         # Not a good idea to use KIcon for fallback since KIcon doesn't take 
  80.         # absolute paths...
  81.         elif os.path.exists('data/icons/22x22/apps/jockey-kde.png') and os.path.exists('data/icons/48x48/apps/jockey-kde.png'):
  82.             icon = KIcon('data/icons/22x22/apps/jockey-kde.png')
  83.             largeIcon = KIcon('data/icons/48x48/apps/jockey-kde.png')
  84.  
  85.         # load tray icon
  86.         # not visible yet
  87.         self.trayicon = KSystemTrayIcon(icon, self.mw)
  88.         #print self.trayicon
  89.         # connect the tray signals
  90.         self.mw.connect(self.trayicon,
  91.                         SIGNAL('activated(QSystemTrayIcon::ActivationReason)'),
  92.                         self.open_app)
  93.  
  94.         self.mw.connect(self.trayicon,
  95.                         SIGNAL('messageClicked()'), self.open_app)
  96.  
  97.         # Set window icon
  98.         self.mw.setWindowIcon(KIcon(icon))
  99.         
  100.         # connect signals
  101.         self.mw.connect(self.mw.buttonBox, SIGNAL('rejected()'), self.on_buttonBox_rejected)
  102.         self.mw.connect(self.mw.buttonBox, SIGNAL('helpRequested()'),
  103.                         self.on_buttonBox_helpRequested)
  104.         
  105.         self.mw.connect(self.mw.button_toggle, SIGNAL('clicked()'),
  106.                           self.on_button_toggle_clicked)
  107.         
  108.         # Set Logo
  109.         self.mw.logo_image.setPixmap(largeIcon.pixmap(48,48))
  110.  
  111.         # disable help button if help is not available
  112.         if not OSLib.inst.ui_help_available(self):
  113.             self.mw.buttonBox.setStandardButtons(KDialogButtonBox.Close)
  114.  
  115.         # load progress dialog UI
  116.         self.progress_ui = ProgressDialog(self.mw)
  117.         
  118.         # load license dialog UI
  119.         self.dialog_licensetext_ui = LicenseDialog(self.mw)
  120.         self.dialog_licensetext_ui.setWindowTitle(self.string_license_dialog_title)
  121.         
  122.         # connect signals
  123.         self.mw.connect(self.mw.linkbutton_licensetext, SIGNAL('leftClickedUrl()'),
  124.                         self.on_linkbutton_licensetext_leftClickedUrl)
  125.         
  126.         
  127.         # connect the signals
  128.         self.progress_ui.connect(self.progress_ui.buttonCancel, SIGNAL('clicked()'),
  129.                           self.on_buttonCancel_clicked)
  130.         
  131.         # we have to connect this signal here, but that still is a
  132.         # bad solution.
  133.         self.mw.connect(self.mw.treeview_drivers,
  134.                         SIGNAL('currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem* )'),
  135.                         self.on_treeview_drivers_currentItemChanged)
  136.         
  137.         self.mw.setWindowTitle(self.main_window_title())
  138.         self.mw.label_license_label.setText(self.string_license_label)
  139.         self.mw.linkbutton_licensetext.setText('(%s)' % self.string_details)
  140.     
  141.     def set_help_button_visible(self, buttonbox, is_visible):
  142.         for myButton in buttonbox.buttons():
  143.             # if it is a help button
  144.             if buttonbox.buttonRole(myButton) == 4:
  145.                 myButton.setVisible(is_visible)
  146.     
  147.     def ui_show_main(self):
  148.         '''Show the main window.'''
  149.  
  150.         # update tree model
  151.         self.update_driver_info_ui(None)
  152.         self.update_tree_model()
  153.         
  154.         # show help button?
  155.         if not OSLib.inst.ui_help_available(self):
  156.             self.set_help_button_visible(self.mw.buttonBox, False)
  157.         
  158.         self.mw.show()
  159.  
  160.     def ui_main_loop(self):
  161.         '''Main loop for the user interface.'''
  162.  
  163.         KApplication.exec_() #MIGHT NEED TO INITIALISE KApp at some point
  164.  
  165.     def error_message(self, title, text):
  166.         '''Present an error message box.'''
  167.  
  168.         text = '<h3>' + title + '</h3>' + text
  169.  
  170.         msgbox = KMessageBox.sorry(self.mw, text, title, KMessageBox.Notify)
  171.  
  172.         # KMessageBox returns nothing in this case, but when it closes we can return true (I think...)
  173.         return True
  174.  
  175.  
  176.     def confirm_action(self, title, text, subtext=None, action=None):
  177.         '''Present a confirmation dialog.
  178.  
  179.         If action is given, it is used as button label instead of the default
  180.         'Continue'.  Return True if the user confirms, False otherwise.
  181.         '''
  182.         text = '<h3>%s</h3>' % text
  183.         if subtext:
  184.             text += subtext
  185.         text = text.replace('\n', '<br/>')
  186.         # FIXME: action doesn't replace the default button label "continue".
  187.         # The Hardy release doesn't either though...
  188.         msgbox = KMessageBox.warningContinueCancel(self.mw, text, title)
  189.  
  190.         # If continue was clicked msgbox returns 5. It returns 2 when cancel is clicked.
  191.         if msgbox == 5:
  192.             return True
  193.         elif msgbox == 2:
  194.             return False
  195.  
  196.     def ui_notification(self, title, text):
  197.         '''Present a notification popup.
  198.  
  199.         This should preferably create a tray icon. Clicking on the tray icon or
  200.         notification should run the GUI.
  201.         '''
  202.         self.trayicon.show()
  203.         self.ui_idle()
  204.         QTimer.singleShot(2000, lambda: self.trayicon.showMessage(title, text))
  205.  
  206.     def open_app(self, argument = None):
  207.         '''Tray activation callback, launch the elevated app.'''
  208.  
  209.         # TODO: this only works with a KDE PolicyKit, which does not exist yet;
  210.         # so work around by calling it through kdesu
  211.         # self.ui_show_main()
  212.         argv = ['/usr/lib/kde4/libexec/kdesu', sys.argv[0]]
  213.         try:
  214.             import subprocess
  215.             subprocess.call(argv, close_fds=True)
  216.         except OSError, e:
  217.             logging.error('could not execute %s: %s' % (str(argv), e.message))
  218.         sys.exit(0)
  219.  
  220.     def ui_idle(self):
  221.         '''Redraw app while external package manager progresses.'''
  222.  
  223.         KApplication.processEvents()    
  224.  
  225.     def ui_progress_start(self, title, description, total):
  226.         '''Create a progress dialog.'''
  227.  
  228.         # hint: you achieve the indeterminate value by setting
  229.         # both min and max to 0
  230.  
  231.         if(total == -1):
  232.             total = 0
  233.  
  234.         self.progress_ui.progressBar.setRange(0, total)
  235.  
  236.         # set translations
  237.         self.progress_ui.setWindowTitle(title)
  238.         self.progress_ui.description.setText(description)
  239.  
  240.         self.progress_ui.show()
  241.         self.cancel_progress = False 
  242.  
  243.     def ui_progress_update(self, current, total):
  244.         '''Update status of current progress dialog.
  245.         
  246.         current/total specify the number of steps done and total steps to
  247.         do, or -1 if it cannot be determined. In this case the dialog should
  248.         display an indeterminated progress bar (bouncing back and forth).
  249.  
  250.         This should return True to cancel, and False otherwise.
  251.         '''
  252.         if current < 0 or total < 0:
  253.             # indeterminated mode
  254.             if self.progress_ui.progressBar.maximum() != 0:
  255.                 self.progress_ui.progressBar.setRange(0, 0)
  256.         else:
  257.             self.progress_ui.progressBar.setRange(0, total)
  258.             self.progress_ui.progressBar.setValue(current)
  259.  
  260.         return self.cancel_progress
  261.  
  262.     def ui_progress_finish(self):
  263.         '''Close the current progress dialog.'''
  264.  
  265.         self.progress_ui.close()
  266.  
  267.     # callbacks
  268.     def on_buttonBox_rejected(self):
  269.         '''Callback for clicked Close button.'''
  270.  
  271.         self.mw.close()
  272.         # Ugly hack since pyqt doesn't seem to exit the main loop
  273.         # without segfaulting since the class is not a subclass of
  274.         # a QDialog or of something related to QT.
  275.         if __name__ == '__main__':# don't break the tests
  276.             sys.exit()
  277.     
  278.     def on_buttonBox_helpRequested(self):
  279.         '''Callback for clicked Help button.'''
  280.  
  281.         OSLib.inst.ui_help(self)
  282.         return True
  283.  
  284.     def on_button_toggle_clicked(self):    
  285.         self.mw.treeview_drivers.setDisabled(True)
  286.         
  287.         item = self.mw.treeview_drivers.currentItem()
  288.         
  289.         if item:
  290.             h_id = item.handler_id
  291.                     
  292.             if self.set_handler_enable(h_id, 'toggle', False):
  293.                 self.update_tree_model()
  294.             self.mw.treeview_drivers.setDisabled(False)
  295.         return True
  296.    
  297.     def on_treeview_drivers_currentItemChanged(self, old, new):
  298.         if new:
  299.             item = self.mw.treeview_drivers.currentItem()
  300.             if item:
  301.                 h_id = item.handler_id
  302.                 self.update_driver_info_ui(h_id)
  303.         return True
  304.     
  305.     def on_buttonCancel_clicked(self):
  306.     
  307.         '''Callback for cancelling the progress dialog.'''
  308.  
  309.         self.cancel_progress = True
  310.         return True
  311.  
  312.     def update_tree_model(self):
  313.         '''Updates the tree model with up to date information.
  314.  
  315.         Unfortunately, due to time contraints, the KDE frontend does not use a
  316.         Model/View framework yet.'''
  317.  
  318.         # we have to workaround the model/view with a subclassed
  319.         # QTreeWidgetItem
  320.         
  321.         self.mw.label_heading.setText('<b>%s</b><br/><br/>%s' % self.main_window_text())
  322.  
  323.         # keep current selection if we have one
  324.         cur_item = self.mw.treeview_drivers.currentItem()
  325.         if cur_item:
  326.             cur_handler = cur_item.handler_id
  327.             cur_item = None
  328.         else:
  329.             cur_handler = None
  330.  
  331.         # first clear the list
  332.         self.mw.treeview_drivers.clear()
  333.  
  334.         # then repopulate
  335.         ha = []
  336.         parents = {}
  337.         for h_id in self.get_displayed_handlers():
  338.             info = self.get_ui_driver_info(h_id)
  339.             if info['needs_reboot']:
  340.                 icon = KIcon('system-restart')
  341.             elif info['enabled']:
  342.                 icon = KIcon('jockey-enabled')
  343.             else:
  344.                 icon = KIcon('jockey-disabled')
  345.  
  346.             # load the item
  347.             # here we make use of the subclassed QTreeWidgetItem
  348.             i = JockeyTreeWidgetItem(h_id, self.mw.treeview_drivers)
  349.             i.setIcon(0, icon)
  350.             i.setText(1, info['name'])
  351.             if h_id == cur_handler:
  352.                 cur_item = i
  353.             
  354.         # Sort the items in alphabetical order
  355.         self.mw.treeview_drivers.sortItems (1, PyQt4.QtCore.Qt.AscendingOrder)
  356.         
  357.         # We want to adjust the columns and expand the tree.
  358.         self.mw.treeview_drivers.expandAll()
  359.         self.mw.treeview_drivers.resizeColumnToContents(0)
  360.         self.mw.treeview_drivers.resizeColumnToContents(1)
  361.  
  362.         # if we previously had a selection, restore it, otherwise select the
  363.         # first one
  364.         if not cur_item:
  365.             cur_item = self.mw.treeview_drivers.topLevelItem(0)
  366.         if cur_item:
  367.             self.mw.treeview_drivers.setItemSelected(cur_item, True)
  368.             self.update_driver_info_ui(cur_item.handler_id)
  369.             
  370.     def update_driver_info_ui(self, handler_id):
  371.         '''Update UI elements which show the driver details.
  372.  
  373.         If handler_id is None, then no driver is selected, no information
  374.         shown, and the appropriate controls are disabled.'''
  375.  
  376. #        info = self.get_ui_driver_info(unicode(handler_id))
  377.         info = self.get_ui_driver_info(handler_id)
  378.         self.mw.label_drivername.setText('<b>%s</b>' % info['name'])
  379.         self.current_driver_name = info['name']
  380.         self.mw.textview_description.setText(info['description'])
  381.         if info['certified'] == None:
  382.             self.mw.image_certification.hide()
  383.         elif info['certified']:
  384.             self.mw.image_certification.show()
  385.             
  386.             if os.path.exists('/usr/share/icons/hicolor/scalable/emblems/jockey-certified.svg'):
  387.                 certified_icon = KIcon('jockey-certified.svg')
  388.                 
  389.                 self.mw.image_certification.setPixmap(certified_icon.pixmap(16,16))
  390.             elif os.path.exists('data/icons/scalable/emblems/jockey-certified.svg'):
  391.                 certified_icon = PyQt4.QtGui.QIcon('data/icons/scalable/emblems/jockey-certified.svg')
  392.                 
  393.                 self.mw.image_certification.setPixmap(certified_icon.pixmap(16,16))
  394.         else:
  395.             self.mw.image_certification.show()
  396.             
  397.             warning_icon = KIcon('dialog-warning')
  398.             self.mw.image_certification.setPixmap(warning_icon.pixmap(16,16))
  399.         
  400.         self.mw.label_certification.setText(info['certification_label'])
  401.         
  402.         if info['free'] == None:
  403.             self.mw.image_license.hide()
  404.             self.mw.label_license_label.hide()
  405.         elif info['free']:
  406.             self.mw.image_license.show()
  407.             self.mw.label_license_label.show()
  408.             self.mw.image_license.setPixmap(KIcon('jockey-free').pixmap(16,16))
  409.         else:
  410.             self.mw.image_license.show()
  411.             self.mw.label_license_label.show()
  412.             
  413.             if os.path.exists('/usr/share/icons/hicolor/scalable/emblems/jockey-proprietary.svg'):
  414.                 proprietary_icon = KIcon('jockey-proprietary.svg')
  415.                 
  416.                 self.mw.image_license.setPixmap(proprietary_icon.pixmap(16,16))
  417.             elif os.path.exists('data/icons/scalable/emblems/jockey-proprietary.svg'):
  418.                 proprietary_icon = PyQt4.QtGui.QIcon('data/icons/scalable/emblems/jockey-proprietary.svg')
  419.                 self.mw.image_license.setPixmap(proprietary_icon.pixmap(16,16))
  420.                 
  421.         self.mw.label_license.setText(info['license_label'])
  422.         
  423.         if info['license_text']:
  424.             self.mw.linkbutton_licensetext.show()
  425.             self.current_license_text = info['license_text']
  426.         else:
  427.             self.mw.linkbutton_licensetext.hide()
  428.             self.current_license_text = None
  429.         if info['enabled'] == None:
  430.             self.mw.image_enabled.hide()
  431.         elif info['needs_reboot']:
  432.             self.mw.image_enabled.show()
  433.             self.mw.image_enabled.setPixmap(KIcon('system-restart').pixmap(16,16))
  434.         elif info['enabled']:
  435.             self.mw.image_enabled.show()
  436.             self.mw.image_enabled.setPixmap(KIcon('jockey-enabled').pixmap(16,16))
  437.         else:
  438.             self.mw.image_enabled.show()
  439.             self.mw.image_enabled.setPixmap(KIcon('jockey-disabled').pixmap(16,16))
  440.         self.mw.label_status.setText(info['status_label'])
  441.         
  442.         if not info['button_toggle_label']:
  443.             self.mw.button_toggle.setText(self.string_button_enable)
  444.             self.mw.button_toggle.setEnabled(False)
  445.         else:
  446.             self.mw.button_toggle.setEnabled(True)
  447.             self.mw.button_toggle.setText(info['button_toggle_label'])
  448.             if info['enabled']:
  449.                 icon = KIcon('jockey-disabled')
  450.                 self.mw.button_toggle.setIcon(icon)
  451.             else:
  452.                 icon = KIcon('jockey-enabled')
  453.                 self.mw.button_toggle.setIcon(icon)
  454.  
  455.     def on_linkbutton_licensetext_leftClickedUrl(self):
  456.         self.dialog_licensetext_ui.label_license_drivername.setText('<b>%s</b>' %
  457.             self.current_driver_name)
  458.         self.dialog_licensetext_ui.textview_license_text.setPlainText(self.current_license_text)
  459.         self.dialog_licensetext_ui.show()
  460.         return True
  461.  
  462. appName = 'jockey'
  463. catalog = ''
  464. programName = ki18n ('Jockey')
  465. version = '1.0'
  466. description = ki18n ('Jockey driver manager')
  467. license = KAboutData.License_GPL
  468. copyright = ki18n('(c) 2007, 2008 Canonical Ltd')
  469. text = ki18n ('none')
  470. homePage = 'https://launchpad.net/jockey'
  471. #bugEmail = ""
  472.  
  473. #generates the about data entry from the provided information.
  474. aboutData = KAboutData(appName, catalog, programName, version, description, license, copyright, text, homePage)
  475.  
  476. aboutData.addAuthor(ki18n('Martin Pitt'), ki18n('Main author'))
  477. aboutData.addAuthor(ki18n('Jonathan Thomas'), ki18n('Work on PyQt4 to PyKDE4 port')) 
  478. aboutData.addAuthor(ki18n('Alberto Milone'), ki18n('Work on PyQt4 to PyKDE4 port'))
  479.  
  480. if __name__ == '__main__':
  481.     # We need a copy of sys.argv for ui.py to use
  482.     argscopy = sys.argv
  483.     # We don't want KCmdLineArgs to parse our cmd line args but it'll need 
  484.     # something... Just give it nothing and let ui.py worry about args.
  485.     sys.argv = [""]
  486.  
  487.     KCmdLineArgs.init(sys.argv, aboutData)
  488.     kapp = KApplication()
  489.  
  490.     sys.argv = argscopy
  491.     OSLib.inst = OSLib()
  492.     u = KDEUI()
  493.     sys.exit(u.run())
  494.